﻿using System;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;

/// <summary>
/// author: 刘时印
/// des ：可复用ListView
/// </summary>
public class CommonListView : MonoBehaviour
{
    private ScrollRect innerScrollRect;
    private RectTransform viewPort;
    // 内容区域
    private RectTransform innerContent;
    // item模板
    public RectTransform itemTemplate;
    // item间距
    public float itemSpace = 5;
    // 滚动方向
    public bool isVertical = true;
    private Vector2 lastTouchPos = new Vector2();
    private Vector2 visibleSize = new Vector2();
    private int spawnNums = 0;

    class ItemData
    {
        public int index = 0;
        public RectTransform item = null;
    }

    private List<ItemData> itemList = new List<ItemData>();
    // 
    public delegate void UpdateCallback(RectTransform item, int index);
    public delegate void ValueChangeCallback(Vector2 delta);
    // 比占满屏幕的多生成几个
    private static int PRE_SPAWN_NUMS = 4;
    // 安全偏移
    private static int SAFE_OFFSET = 0;
    // 往上拖动
    private static int DIR_UP = -1;
    // 往下拖动
    private static int DIR_DOWN = 1;
    // 往左拖动
    private static int DIR_LEFT = 1;
    // 往拖动
    private static int DIR_RIGHT = -1;

    private UpdateCallback updateItemCallback = null;
    private ValueChangeCallback valueChnageCallback;

    void Start()
    {
        InitListView();
        print(GetContentSize());
        ReSize(30);

    }

    public bool IsVertical
    {
        set
        {
            SetDirection(isVertical);
        }
        get
        {
            return isVertical;
        }
    }

    float CalDragDirction(Vector2 lastPos, Vector2 curPos)
    {
        float dir = 0;
        if (isVertical)
        {
            dir = curPos.y - lastPos.y;
        }
        else
        {
            dir = curPos.x - lastPos.x;
        }
        return Mathf.Abs(dir) / dir;
    }

    void OnValueChange(Vector2 delta)
    {
        float dir = CalDragDirction(lastTouchPos, delta);
        lastTouchPos = delta;
        var myItemArrays = itemList.ToArray();
        for(int i=0;i<myItemArrays.Length;++i)
        {
            if (isVertical)
            {
                DoVerticalResetPosition(ref myItemArrays[i], dir);
            }
            else
            {
                DoHorizontalResetPosition(ref myItemArrays[i], dir);
            }
        }
        if(valueChnageCallback!=null)
        {
            valueChnageCallback(delta);
        }
    }

    // 相对于父节点的位置
    Vector3 GetItemRelativeParentPos(RectTransform item)
    {
        Vector3 parentPos = innerContent.localPosition;
        return parentPos + item.localPosition;
    }

    void DoVerticalResetPosition(ref ItemData itemData, float dir)
    {
        if (dir == DIR_UP)   // 往上拖动
        {
            if (GetItemRelativeParentPos(itemData.item).y > (GetRectTranformSize(itemTemplate).y + itemSpace + SAFE_OFFSET))
            {
                var newPos = new Vector2(0, (GetItemRelativeParentPos(itemData.item).y - visibleSize.y) - GetInnerContentPosition().y);
                if (newPos.y >= -(GetInnerContentSize().y - (GetRectTranformSize(itemTemplate).y + itemSpace)))
                {
                    itemData.item.localPosition = newPos;
                    int index = itemData.index + spawnNums;
                    OnUpdateItem(ref itemData, index);
                }
            }
        }
        else if (dir == DIR_DOWN)   // 往下拖动
        {
            if (GetItemRelativeParentPos(itemData.item).y < -(GetContentSize().y + SAFE_OFFSET))
            {
                var newPos = new Vector2(0, (GetItemRelativeParentPos(itemData.item).y + visibleSize.y) - GetInnerContentPosition().y);
                if (newPos.y <= 0)
                {
                    itemData.item.localPosition = newPos;
                    int index = itemData.index - spawnNums;
                    OnUpdateItem(ref itemData, index);
                }
            }
        }
    }

    void DoHorizontalResetPosition(ref ItemData itemData, float dir)
    {
        if (dir == DIR_LEFT)    // 往左拖动
        {
            if (GetItemRelativeParentPos(itemData.item).x < -(GetRectTranformSize(itemTemplate).x + itemSpace + SAFE_OFFSET))
            {
                var newPos = new Vector2((GetItemRelativeParentPos(itemData.item).x + visibleSize.x) - GetInnerContentPosition().x, 0);
                if (newPos.x <= (GetInnerContentSize().x - (GetRectTranformSize(itemTemplate).x + itemSpace)))
                {
                    itemData.item.localPosition = newPos;
                    int index = itemData.index + spawnNums;
                    OnUpdateItem(ref itemData, index);
                }
            }
        }
        else if (dir == DIR_RIGHT)  // 往右拖动
        {
            if (GetItemRelativeParentPos(itemData.item).x > (GetContentSize().x + SAFE_OFFSET))
            {
                var newPos = new Vector2((GetItemRelativeParentPos(itemData.item).x - visibleSize.x) - GetInnerContentPosition().x, 0);
                if (newPos.x >= 0)
                {
                    itemData.item.localPosition = newPos;
                    int index = itemData.index - spawnNums;
                    OnUpdateItem(ref itemData, index);
                }
            }
        }
    }

    void InitListView()
    {
        innerScrollRect = GetComponent<ScrollRect>();
        Transform viewPortObj = transform.Find("Viewport");
        viewPort = viewPortObj.GetComponent<RectTransform>();
        innerContent = viewPortObj.Find("Content").GetComponent<RectTransform>();

        innerScrollRect.content.anchorMax = new Vector2(0, 1);
        innerScrollRect.content.anchorMin = new Vector2(0, 1);
        innerScrollRect.content.pivot = new Vector2(0, 1);

        SetInnerContentPositionY(0);
        SetInnerContentPositionX(0);

        ScrollRect.ScrollRectEvent scrollEvent = new ScrollRect.ScrollRectEvent();
        scrollEvent.AddListener(OnValueChange);
        innerScrollRect.onValueChanged = scrollEvent;

        innerContent.anchorMin = new Vector2(0, 1);
        innerContent.anchorMax = new Vector2(0, 1);
        innerContent.pivot = new Vector2(0, 1);

        SetDirection(isVertical);
    }

    public void SetDirection(bool isVertical)
    {
        this.isVertical = isVertical;
        innerScrollRect.vertical = isVertical;
        innerScrollRect.horizontal = !isVertical;
    }

    void SetInnerContentPositionY(float y)
    {
        var prePos = innerContent.localPosition;
        prePos.y = y;
        innerContent.localPosition = prePos;
    }

    void SetInnerContentPositionX(float x)
    {
        var prePos = innerContent.localPosition;
        prePos.x = x;
        innerContent.localPosition = prePos;
    }

    void AddItem(int index)
    {
        ItemData itemData = new ItemData();
        RectTransform item = Instantiate<RectTransform>(itemTemplate, innerContent.transform);
        item.anchorMin = new Vector2(0f, 1);
        item.anchorMax = new Vector2(0f, 1);
        item.pivot = new Vector2(0, 1);
        item.localPosition = GetItemPosition();
        item.name = item.name + index;
        itemData.item = item;
        OnUpdateItem(ref itemData, index);
        itemList.Add(itemData);
    }

    Vector2 GetItemPosition()
    {
        Vector2 itemPos = new Vector2(0, 0);
        foreach (var item in itemList)
        {
            Vector2 itemSize = GetRectTranformSize(item.item);
            if (isVertical)
            {
                itemPos.y -= (itemSize.y + itemSpace);
            }
            else
            {
                itemPos.x += (itemSize.x + itemSpace);
            }
        }
        return itemPos;
    }

    public Vector2 GetInnerContentSize()
    {
        return GetRectTranformSize(innerContent);
    }

    public Vector3 GetInnerContentPosition()
    {
        return innerContent.localPosition;
    }

    public Vector2 GetContentSize()
    {
        return GetRectTranformSize(GetComponent<RectTransform>());
    }

    Vector2 GetRectTranformSize(RectTransform tran)
    {
        Rect itemRect = tran.rect;
        return new Vector2(Mathf.Abs(itemRect.xMax - itemRect.xMin), Mathf.Abs(itemRect.yMax - itemRect.yMin));
    }

    public void ReSize(int nums)
    {
        if(itemTemplate==null)
        {
            return;
        }
        ClearItem();
        SpawnItem(nums);
        SetInnerContentSize(CalculateInnerContentSize(nums));
    }

    void ClearItem()
    {
        foreach(var item in itemList)
        {
            Destroy(item.item.gameObject);
        }
        itemList.Clear();
    }

    void SpawnItem(int nums)
    {
        Vector2 itemSize = GetRectTranformSize(itemTemplate);
        Vector2 contentSize = GetContentSize();
        int needsNums = 0;
        if (isVertical)
        {
            needsNums = (int)(contentSize.y / (itemSize.y + itemSpace)) + PRE_SPAWN_NUMS;
        }
        else
        {
            needsNums = (int)(contentSize.x / (itemSize.x + itemSpace)) + PRE_SPAWN_NUMS;
        }
        spawnNums = nums > needsNums ? needsNums : nums;
        for (int i = 0; i < spawnNums; ++i)
        {
            AddItem(i);
        }
        if (isVertical)
        {
            visibleSize.x = itemSize.x;
            visibleSize.y = spawnNums * itemSize.y + itemSpace * spawnNums;
        }
        else
        {
            visibleSize.x = spawnNums * itemSize.x + itemSpace * spawnNums;
            visibleSize.y = itemSize.y;
        }
    }

    void OnUpdateItem(ref ItemData itemData,int index)
    {
        itemData.index = index;
        //测试代码
        var text = itemData.item.transform.GetComponentInChildren<Text>();
        text.text = "" + index;
        if (updateItemCallback!=null)
        {
            updateItemCallback(itemData.item, index + 1);
        }
    }

    void SetItemUpdateCallback(UpdateCallback callback)
    {
        if(callback!=null)
        {
            updateItemCallback = callback;
        }
    }

    void SetValueChangeCallback(ValueChangeCallback callback)
    {
        if(callback!=null)
        {
            valueChnageCallback = callback;
        }
    }

    Vector2 CalculateInnerContentSize(int nums)
    {
        Vector2 itemSize = GetRectTranformSize(itemTemplate);
        Vector2 contentSize = GetContentSize();
        var innerSize = new Vector2();
        if (isVertical)
        {
            innerSize.x = contentSize.x;
            innerSize.y = itemSize.y * nums + itemSpace * nums;
        }
        else
        {
            innerSize.x = itemSize.x * nums + itemSpace * nums;
            innerSize.y = contentSize.y;
        }
        return innerSize;
    }

    void SetInnerContentSize(Vector2 size)
    {
        innerContent.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, size.y);
        innerContent.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, size.x);
        if (isVertical)
        {
            SetInnerContentPositionY(0);
        }
        else
        {
            SetInnerContentPositionX(0);
        }
    }
}
